package com.aspire.android.heliar.utils;

/*
 * @(#)JdkZipUtil.java
 * 
 * Create Version: 1.0.0
 * Author: James Liu
 * Create Date: 2008-03-18
 * 
 * Copyright (c) 2008 UTStarcom(China) Corporation. All Right Reserved.
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * ������Ǵ���Zip�Ĺ����ࡣ
 * 
 * ѹ���ļ��ڵ��ļ����Ŀ¼��ֻ��ʹ��ASCII��UTF-8�� ��ΪJDK��zipʵ���ǰ���UTF-8��ȡ�ģ�
 * ����GBK�ȱ����ַ����³����쳣��
 * 
 * @version 1.0.0 2008-03-18
 * @author James Liu
 */
public class ZipUtil {

	private static final int BUFFER_SIZE = 1024;

	/**
	 * ���ļ���Ŀ¼ѹ����ѹ���ļ��С�
	 * 
	 * ����Ĭ��ѹ������Ŀ¼��ѹ�����ļ�ѹ����
	 * 
	 * @param source
	 *            Ҫѹ�����ļ���Ŀ¼
	 * @param zipFile
	 *            Ŀ��ѹ���ļ�
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void compress(File source, File zipFile) throws IOException,
			ZipException {

		File[] files = new File[1];
		files[0] = source;
		compress(files, zipFile, null);
	}

	/**
	 * ���ļ���Ŀ¼ѹ����ѹ���ļ��С�
	 * 
	 * @param source
	 *            Ҫѹ�����ļ���Ŀ¼
	 * @param zipFile
	 *            Ŀ��ѹ���ļ�
	 * @param rules
	 *            ͨ��������ʽָ�������ڷ�ϱ��ʽEntry�Ƿ�ѹ���Ĺ���
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void compress(File source, File zipFile,
			Map<String, Boolean> rules) throws IOException, ZipException {

		File[] files = new File[1];
		files[0] = source;
		compress(files, zipFile, rules);
	}

	/**
	 * ��һ�������ļ���Ŀ¼ѹ����ѹ���ļ��С�
	 * 
	 * ����Ĭ��ѹ������Ŀ¼��ѹ�����ļ�ѹ����
	 * 
	 * @param fileList
	 *            Ҫѹ�����ļ���Ŀ¼��һ��������
	 * @param zipFile
	 *            Ŀ��ѹ���ļ�
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void compress(List<File> fileList, File zipFile)
			throws IOException, ZipException {

		if (fileList == null) {
			throw new IOException("Parameter[fileList] is null!");
		} else {
			compress(fileList.toArray(new File[0]), zipFile, null);
		}
	}

	/**
	 * ��һ�������ļ���Ŀ¼ѹ����ѹ���ļ��С�
	 * 
	 * @param fileList
	 *            Ҫѹ�����ļ���Ŀ¼��һ��������
	 * @param zipFile
	 *            Ŀ��ѹ���ļ�
	 * @param rules
	 *            ͨ��������ʽָ�������ڷ�ϱ��ʽEntry�Ƿ�ѹ���Ĺ���
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void compress(List<File> fileList, File zipFile,
			Map<String, Boolean> rules) throws IOException, ZipException {

		if (fileList == null) {
			throw new IOException("Parameter[fileList] is null!");
		} else {
			compress(fileList.toArray(new File[0]), zipFile, rules);
		}
	}

	/**
	 * ��һ�������ļ���Ŀ¼ѹ����ѹ���ļ��С�
	 * 
	 * ����Ĭ��ѹ������Ŀ¼��ѹ�����ļ�ѹ����
	 * 
	 * @param files
	 *            Ҫѹ�����ļ���Ŀ¼��һ��������
	 * @param zipFile
	 *            Ŀ��ѹ���ļ�
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void compress(File[] files, File zipFile) throws IOException,
			ZipException {

		compress(files, zipFile, null);
	}

	/**
	 * ��һ�������ļ���Ŀ¼ѹ����ѹ���ļ��С�
	 * 
	 * @param files
	 *            Ҫѹ�����ļ���Ŀ¼��һ��������
	 * @param zipFile
	 *            Ŀ��ѹ���ļ�
	 * @param rules
	 *            ͨ��������ʽָ�������ڷ�ϱ��ʽEntry�Ƿ�ѹ���Ĺ���
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void compress(File[] files, File zipFile,
			Map<String, Boolean> rules) throws IOException, ZipException {

		if (files == null) {
			throw new IOException("Parameter[files] is null!");
		} else {
			for (int i = 0; i < files.length; i++) {
				if (files[i] == null) {
					throw new IOException("Parameter[files] is null!");
				} else if (!files[i].exists()) {
					throw new IOException("Parameter[files] did not exist!");
				}
			}
		}
		if (zipFile == null) {
			throw new IOException("Parameter[zipFile] is null!");
		} else if (zipFile.exists() && !zipFile.isFile()) {
			throw new IOException("Parameter[zipFile] is not a file!");
		}

		ZipOutputStream os = new ZipOutputStream(new BufferedOutputStream(
				new FileOutputStream(zipFile)));
		try {
			for (int i = 0; i < files.length; i++) {
				addZipEntry(files[i], null, os, rules);
			}
		} finally {
			try {
				os.close();
			} catch (Exception e) {
			}
		}
	}

	private static void addZipEntry(File source, String parent,
			ZipOutputStream os, Map<String, Boolean> rules) throws IOException,
			ZipException {

		String zipEntryName = "";
		if ((parent == null) || "".equals(parent.trim())) {
			zipEntryName = source.getName();
		} else {
			zipEntryName = parent + source.getName();
		}
		if (source.isDirectory()) {
			zipEntryName = zipEntryName + "/";
		}

		ZipEntry zipEntry = new ZipEntry(zipEntryName);
		if (needCompress(source, rules)) {
			zipEntry.setMethod(ZipEntry.DEFLATED);
		} else {
			zipEntry.setMethod(ZipEntry.STORED);
		}

		if (source.isFile()) {
			zipEntry.setSize(source.length());
			zipEntry.setCrc(getCrc32(source));
			os.putNextEntry(zipEntry);

			BufferedInputStream is = new BufferedInputStream(
					new FileInputStream(source));
			try {
				byte[] buf = new byte[BUFFER_SIZE];
				int count = -1;
				while ((count = is.read(buf, 0, BUFFER_SIZE)) != -1) {
					os.write(buf, 0, count);
				}
			} finally {
				try {
					is.close();
				} catch (Exception e) {
				}
			}
		} else if (source.isDirectory()) {
			zipEntry.setSize(0);
			zipEntry.setCrc(0);
			os.putNextEntry(zipEntry);

			File[] files = source.listFiles();
			for (int i = 0; i < files.length; i++) {
				addZipEntry(files[i], zipEntryName, os, rules);
			}
		}
	}

	private static boolean needCompress(File file, Map<String, Boolean> rules) {

		boolean result = false;

		if (file.isDirectory()) {
			result = false;
		} else if (file.isFile()) {
			if (rules == null) {
				result = true;
			} else {
				boolean matchedRule = false;
				Iterator<String> ruleIter = rules.keySet().iterator();
				while (ruleIter.hasNext()) {
					String rule = ruleIter.next();
					if (file.getAbsolutePath().matches(rule)) {
						matchedRule = true;
						result = rules.get(rule);
						break;
					}
				}
				if (!matchedRule) {
					result = needCompress(file, null);
				}
			}
		}

		return result;
	}

	private static long getCrc32(File file) throws IOException {

		long result = 0;

		if (file.isFile()) {
			CRC32 crc = new CRC32();
			BufferedInputStream is = new BufferedInputStream(
					new FileInputStream(file));
			try {
				byte[] buf = new byte[BUFFER_SIZE];
				int count = -1;
				while ((count = is.read(buf, 0, BUFFER_SIZE)) != -1) {
					crc.update(buf, 0, count);
				}

				result = crc.getValue();
			} finally {
				try {
					is.close();
				} catch (Exception e) {
				}
			}
		}

		return result;
	}

	/**
	 * ��ѹ��Zip�ļ���ָ��Ŀ¼��
	 * 
	 * @param zipFile
	 *            Ҫ��ѹ��Zip�ļ�
	 * @param targetDir
	 *            ��ѹ��ָ����Ŀ¼
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void decompress(File zipFile, File targetDir)
			throws IOException, ZipException {

		decompress(zipFile, null, targetDir);
	}

	/**
	 * ��ѹ��Zip�ļ���ĳ��Entry��ָ��Ŀ¼��
	 * 
	 * @param zipFile
	 *            Ҫ��ѹ��Zip�ļ�
	 * @param entryName
	 *            Ҫ��ѹ��Entry����Сд����
	 * @param targetDir
	 *            ��ѹ��ָ����Ŀ¼
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static void decompress(File zipFile, String entryName, File targetDir)
			throws IOException, ZipException {

		if (zipFile == null) {
			throw new IOException("Parameter[zipFile] is null!");
		} else if (!zipFile.exists()) {
			throw new IOException("Parameter[zipFile] did not exist!");
		} else if (!zipFile.isFile()) {
			throw new IOException("Parameter[zipFile] is not a file!");
		} else if (!isZipFile(zipFile)) {
			throw new ZipException("Parameter[zipFile] is not a zip file!");
		} else if (targetDir == null) {
			throw new IOException("Parameter[targetDir] is null!");
		} else if (!targetDir.exists() && !targetDir.mkdirs()) {
			throw new IOException(
					"Can't create directory of the parameter[targetDir]!");
		} else {
			ZipInputStream is = new ZipInputStream(new BufferedInputStream(
					new FileInputStream(zipFile)));
			try {
				ZipEntry zipEntry;
				while ((zipEntry = is.getNextEntry()) != null) {
					if ((entryName != null) && !"".equals(entryName.trim())
							&& !entryName.equals(zipEntry.getName())) {
						continue;
					}

					if (zipEntry.isDirectory()) {
						File dir = new File(targetDir, zipEntry.getName());
						if (!dir.exists() && !dir.mkdirs()) {
							throw new IOException(
									"Can't create directory that was extracted from zip!");
						}
					} else {
						File file = new File(targetDir, zipEntry.getName());
						File dir = file.getParentFile();
						if (!dir.exists() && !dir.mkdirs()) {
							throw new IOException(
									"Can't create directory that was extracted from zip!");
						}

						BufferedOutputStream os = new BufferedOutputStream(
								new FileOutputStream(file));
						try {
							byte[] buf = new byte[BUFFER_SIZE];
							int count = -1;
							while ((count = is.read(buf, 0, BUFFER_SIZE)) != -1) {
								os.write(buf, 0, count);
							}
						} finally {
							try {
								os.close();
							} catch (Exception e) {
							}
						}
					}
				}
			} finally {
				try {
					is.close();
				} catch (Exception e) {
				}
			}
		}
	}

	/**
	 * �г�ZIPѹ���ļ��б��嵥��
	 * 
	 * @param zipFile
	 *            Ŀ��ѹ���ļ�
	 * 
	 * @return ZIPѹ���ļ��б��嵥
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 * @throws ZipException
	 *             ���ļ�����Zip�ļ�ʱ�׳���
	 */
	public static String list(File zipFile) throws IOException, ZipException {

		if (zipFile == null) {
			throw new IOException("Parameter[zipFile] is null!");
		} else if (!zipFile.exists()) {
			throw new IOException("Parameter[zipFile] did not exist!");
		} else if (!zipFile.isFile()) {
			throw new IOException("Parameter[zipFile] is not a file!");
		} else if (!isZipFile(zipFile)) {
			throw new ZipException("Parameter[zipFile] is not a zip file!");
		} else {
			StringBuffer sb = new StringBuffer();
			ZipFile zip = new ZipFile(zipFile);
			Enumeration<? extends ZipEntry> entryEnum = zip.entries();
			while (entryEnum.hasMoreElements()) {
				ZipEntry zipEntry = entryEnum.nextElement();
				sb.append(zipEntry.getName());
				sb.append("\n");
			}
			try {
				zip.close();
			} catch (Exception e) {
			}

			return sb.toString();
		}
	}

	private static boolean isZipFile(File file) {

		try {
			(new ZipFile(file)).close();
			return true;
		} catch (IOException e) {
			return false;
		}
	}
}
